#include "node.h"
#include "edge.h"
#include "graph.h"
#include "attributeset.h"

Node::Node(QString n, Graph* g ) : graph(g), QGraphicsObject( g ), parent(g)
{
    name = n;
    nodeWidth = 40;
    QRectF pRect = parent->boundingRect();
    setFlag(ItemIsMovable);
    setFlag(ItemSendsGeometryChanges);
    setCacheMode(DeviceCoordinateCache);
    setZValue(-1);
    setPos( qrand() % ( (int) (pRect.width()-nodeWidth) ) + pRect.x()+nodeWidth/2, qrand() % ( (int) (pRect.height())-nodeWidth ) + pRect.y()+nodeWidth/2 );
    qDebug() << pos() << endl;
    mainColor.setNamedColor("mediumslateblue");
    borderColor.setNamedColor("darkslateblue");
    QColor penColor;
    penColor.setNamedColor("slategrey");
    outlinePen.setColor( penColor );
    outlinePen.setWidth( 1 );
    setAcceptsHoverEvents(true);
}

Node::~Node()
{

}

//Node::Node(const Node &copy)
//{
//    for(int i = 0; i < copy.edgeList.size(); i++)
//        edgeList << copy.edgeList[i];
//    name = copy.name;
//}

bool Node::advance()
{
    if (newPos == pos())
        return false;

    setPos(newPos);
    return true;
}

QRectF Node::boundingRect() const
{
    qreal adjust = 2;
    return QRectF(-25, -25, 50, 50 );//-15 - adjust, -15 - adjust,
                  //23 + adjust, 23 + adjust);
}

QPainterPath Node::shape() const
{
    QPainterPath path;
    QRectF myRect = boundingRect();
    path.addEllipse(  myRect.x(), myRect.y(), myRect.width(), myRect.height() ); //-10, -10, 20, 20);
    return path;
}

void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{   
    if( attributes.size() == 0 )
    {
        QRadialGradient gradient(-3, -3, 10);
        if (option->state & QStyle::State_Sunken) {
            gradient.setCenter(3, 3);
            gradient.setFocalPoint(3, 3);
            gradient.setColorAt(1, mainColor);//QColor(Qt::cyan).light(120));
            gradient.setColorAt(0, borderColor);//QColor(Qt::darkCyan).light(120));
        } else {
            gradient.setColorAt(0, mainColor);//Qt::cyan);
            gradient.setColorAt(1, borderColor);//Qt::darkCyan);
        }
        painter->setBrush(gradient);
        painter->setPen(outlinePen);//0));//QPen(Qt::darkGray, 0));
        QRectF myRect = boundingRect();
        painter->drawEllipse(-nodeWidth/2, -nodeWidth/2, nodeWidth, nodeWidth);
    }
    else
    {
        painter->setPen( outlinePen );
        painter->drawEllipse(-nodeWidth/2, -nodeWidth/2, nodeWidth, nodeWidth);

        int angleIncr = (360*16)/attributes.size();
        int startAngle = 0;
        for(int i = 0; i < attributes.size(); i++)
        {
            QColor c = attributes[i]->getColor();
            QRadialGradient gradient(-3, -3, 10);
            if (option->state & QStyle::State_Sunken) {
                gradient.setCenter(3, 3);
                gradient.setFocalPoint(3, 3);
                gradient.setColorAt(1, c);//QColor(Qt::cyan).light(120));
                gradient.setColorAt(0, c);//QColor(Qt::black));//QColor(Qt::darkCyan).light(120));
            } else {
                gradient.setColorAt(0, c);//Qt::cyan);
                gradient.setColorAt(1, c );//1, QColor(Qt::black));//Qt::darkCyan);
            }
            QBrush brush(c);
            painter->setBrush( brush );//gradient);
            painter->setPen(QPen(Qt::darkGray, 0));
            QRectF myRect = boundingRect();
            myRect.adjust(+3, +3, -3, -3);
            painter->drawPie(myRect, startAngle, angleIncr);//startAngle, spanAngle);
            startAngle+=angleIncr;
        }
    }

    QFont f;
    f.setPointSize(10);
    painter->setFont(f);
    painter->setPen(QPen(Qt::black) );//Qt::lightGray));
    painter->drawText(boundingRect(), Qt::AlignCenter, name, &boundingRect());
}


void Node::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    update();
    QGraphicsObject::mousePressEvent(event);
}

void Node::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    update();
    QGraphicsObject::mouseReleaseEvent(event);
}

void Node::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    qDebug() << "node dragged to coordinate: " << mapToParent(0, 0) << " in parent coordinate system.  I'm " << name << endl;
    emit( nodeMoved( pos() ) );
    //update();
    QGraphicsObject::mouseMoveEvent(event);
}

QVariant Node::itemChange(GraphicsItemChange change, const QVariant &value)
{
    switch (change) {
    case ItemPositionHasChanged:
        foreach (Edge *edge, edgeList)
            edge->adjust();
        graph->itemMoved();
        break;
    default:
        break;
    };

    return QGraphicsItem::itemChange(change, value);
}

void Node::addEdge(Edge *edge)
{
    edgeList << edge;
    edge->adjust();
}

void Node::addAttribute(QPointer<QualAttribute> att)
{
    attributes.push_back( att );
}

QList<Edge *> Node::edges() const
{
    return edgeList;
}

void Node::calculateForces()
{
    if (!scene() || scene()->mouseGrabberItem() == this) {
        newPos = pos();
        return;
    }

    // Sum up all forces pushing this item away
    qreal xvel = 0;
    qreal yvel = 0;
    foreach (QGraphicsItem *item, scene()->items()) {
        Node *node = qgraphicsitem_cast<Node *>(item);
        if (!node)
            continue;

        QPointF vec = mapToItem(node, 0, 0);
        qreal dx = vec.x();
        qreal dy = vec.y();
        double l = 2.0 * (dx * dx + dy * dy);
        if (l > 0) {
            xvel += (dx * 150.0) / l;
            yvel += (dy * 150.0) / l;
        }
    }

    // Now subtract all forces pulling items together
    double weight = (edgeList.size() + 1) * 10;
    foreach (Edge *edge, edgeList) {
        QPointF vec;
        if (edge->getSourceNode() == this)
            vec = mapToItem(edge->getDestNode(), 0, 0);
        else
            vec = mapToItem(edge->getSourceNode(), 0, 0);
        xvel -= vec.x() / weight;
        yvel -= vec.y() / weight;
    }

    if (qAbs(xvel) < 0.1 && qAbs(yvel) < 0.1)
        xvel = yvel = 0;

    QRectF sceneRect = scene()->sceneRect();
    newPos = pos() + QPointF(xvel, yvel);
    newPos.setX(qMin(qMax(newPos.x(), sceneRect.left() + 10), sceneRect.right() - 10));
    newPos.setY(qMin(qMax(newPos.y(), sceneRect.top() + 10), sceneRect.bottom() - 10));
}

void Node::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    qDebug() << "hover enter event" << endl;
    changeAfterHoverEnter();

    emit( hoverEnterSignal() );

    update();
}

void Node::changeAfterHoverEnter()
{
    qDebug() << "change after hover enter. " << name << endl;
    QColor penColor;
    penColor.setNamedColor("gold");
    outlinePen.setColor( penColor );
    if( attributes.size() > 0 )
        outlinePen.setWidth( 12 );
    else
        outlinePen.setWidth( 5 );
}

void Node::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    qDebug() << "hover leave event" << endl;
    changeAfterHoverLeave();

    emit( hoverLeaveSignal() );

    update();
}

void Node::changeAfterHoverLeave()
{
    qDebug() << "change after hover leave. " << name << endl;
    mainColor.setNamedColor("mediumslateblue");
    borderColor.setNamedColor("darkslateblue");
    QColor penColor;
    penColor.setNamedColor("slategrey");
    outlinePen.setColor( penColor );
    outlinePen.setWidth( 1 );
}

void Node::hoverEnterSlot()
{
    qDebug() << "recieved signal that hover has entered.  I'm " << name << endl;
    changeAfterHoverEnter();
    update();
}

void Node::hoverLeaveSlot()
{
    qDebug() << "recieved signal that hover has left.  I'm " << name << endl;
    changeAfterHoverLeave();
    update();
}

QString Node::getName()
{
    return name;
}

void Node::moveNode(QPointF newPosition)
{
    qDebug() << "I'm mirroring the other node by moving to absolute coordinate: " << mapToScene( mapFromParent(newPosition)).x() << " " << mapToScene( mapFromParent(newPosition)).y() << " I'm " << name << endl;
    QRectF pRect = parent->boundingRect();
    setPos( newPosition );// % ( (int) (pRect.width()-nodeWidth) ) + pRect.x()+nodeWidth/2, newPosition.y() % ( (int) (pRect.height())-nodeWidth ) + pRect.y()+nodeWidth/2 );
}


